<template>
    <a-layout>
        <a-layout-content
                :style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }"
        >
            <p>
                <!-- 这里可以直接用一个name作为响应式变量，但考虑到后续可能通过多个参数来查询，为了扩展性，统一封装成一个param对象 -->
                <a-form layout="inline" :model="param">
                    <a-form-item>
                        <a-input v-model:value="param.name" placeholder="名称" />
                    </a-form-item>
                    <a-form-item>
                        <!-- 下面的 pagination 是响应式变量，在html中试用响应式变量，无需.value -->
                        <a-button type="primary" @click="handleQuery({pageNum:1, pageSize: pagination.pageSize})">查询</a-button>
                    </a-form-item>
                    <a-form-item>
                        <a-button type="primary" @click="handleAdd">新增</a-button>
                    </a-form-item>
                </a-form>
            </p>
            <a-table
              :columns="columns"
              :data-source="ebooks"
              :row-key="record => record.id"
              :pagination="pagination"
              :loading="loading"
              @change="handleTableChange"
            >
                <template #cover="{ record }">
                    <img v-if="record.cover" :src="baseURL+'/ebook/load-image/'+record.cover" alt="avatar"/>
                </template>
                <template v-slot:category="{ text, record }">
                    <span>{{getCategoryName(record.category1Id)}} / {{getCategoryName(record.category2Id)}}</span>
                </template>
                <template #action="{ record }">
                    <a-space size="small">
                        <router-link :to="'/admin/doc?ebookId=' + record.id">
                            <a-button type="primary">文档管理</a-button>
                        </router-link>
                        <a-button type="primary" @click="handleEdit(record)">编辑</a-button>
                        <a-popconfirm
                          title="删除后不可恢复，确认删除?"
                          ok-text="是"
                          cancel-text="否"
                          @confirm="handleDelete(record.id)"
                        >
                            <!-- 在 2.2.0 之后，危险成为一种按钮属性而不是按钮类型 -->
                            <a-button type="primary" danger>删除</a-button>
                        </a-popconfirm>
                    </a-space>
                </template>
            </a-table>
        </a-layout-content>
    </a-layout>
    <a-modal
      title="电子书表单"
      v-model:visible="modalVisible"
      :confirm-loading="modalLoading"
      @ok="handleModalOk">
        <a-form :model="ebook" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
            <a-form-item label="封面">
<!--                <a-input v-model:value="ebook.cover" />-->
<!-- 遗留小问题：新增或修改图片时，上传完成后，ebook.cover会被赋值为新生成的图片名，此时加载图片还取不到信息（因为图片还在temp目录，不在work目录）-->
    <!--  方案：上传图片后的临时加载回显，传个标识，请求后台从temp目录加载 ；其他情况（点击编辑按钮）请求后台从work目录加载 -->
                <img v-if="ebook.cover" :src="baseURL+'/ebook/load-image/'+ebook.cover" alt="avatar"/>
                <a-upload
                        v-model:file-list="fileList"
                        :action="baseURL + '/ebook/upload-image'"
                        list-type="picture"
                        accept=".jpg,.png,.jpeg"
                        :headers="headers"
                        @change="handleUploadChange"
                >
                    <a-button>
                        <upload-outlined></upload-outlined>
                        上传封面图片
                    </a-button>
                    (最大不要超过5M)
                </a-upload>
            </a-form-item>
            <a-form-item label="名称">
                <a-input v-model:value="ebook.name" />
            </a-form-item>
            <!-- :options指定级联选项的数组，:field-names中的label，value，children分别指定每个选项的显示、取值和子属性（即数组中的二级级联） -->
            <a-form-item label="分类">
                <a-cascader
                        v-model:value="categoryIds"
                        :options="level1"
                        :field-names="{ label: 'name', value: 'id', children: 'children' }"
                />
            </a-form-item>
            <a-form-item label="描述">
                <a-input v-model:value="ebook.description" type="textarea"/>
            </a-form-item>
        </a-form>
    </a-modal>
</template>

<script lang="ts">
import { defineComponent, onMounted, ref } from 'vue';
import axios from 'axios';
import { message } from 'ant-design-vue';
import {Tool} from "@/util/tool";
import store from "@/store";

export default defineComponent({
  name: 'AdminEbook',
  setup() {
    const param = ref() ; // 定义一个响应式变量，并初始化为空。用于存放搜索框中的内容）
    param.value = {} ;           //若不初始化，页面加载时param.name会出错，因为页面加载时就会读取该属性值
    const ebooks = ref(); // ref方式，实现数据绑定
    const pagination = ref({
        current: 1,
        pageSize: 3,
        total: 0
    });
    const loading = ref(false);

    // 表格参数：title指定列名；dataIndex指定值渲染，slots为自定义渲染（其中title自定义列名，customRender自定义值；html中要有对应template），key保持唯一即可
    const columns = [
      {
        title: '封面',
        key: 'cover',
        slots: { customRender: 'cover' }
      },
      {
        title: '名称',
        dataIndex: 'name',
        key: 'name',
      },
      {
        title: '分类',
        key: 'category',
        slots: { customRender: 'category' }
      },
      {
        title: '文档数',
        dataIndex: 'docCount',
        key: 'docCount',
      },
      {
        title: '阅读数',
        dataIndex: 'viewCount',
        key: 'viewCount',
      },
      {
        title: '点赞数',
        dataIndex: 'voteCount',
        key: 'voteCount',
      },
      {
        title: 'Action',
        key: 'action',
        slots: { customRender: 'action' },
      },
    ];

    /** 数据查询 */
    const handleQuery = (params: any) => {
        loading.value = true;   // 加载中
        axios.get("/ebook/list", {
            params: {
                pageNum: params.pageNum,
                pageSize: params.pageSize,
                name: param.value.name
            }
        }).then((response) => {
            loading.value = false;  // 加载完成
            const data = response.data ;
            if(data.success){
                ebooks.value = data.content.list ; //ref方式，赋值

                // 重置分页按钮（否则分页栏不会聚焦在当前页）
                pagination.value.current = params.pageNum;
                // 设置总数（否则后端分页后，前端无法得知总页数，会将返回结果list作为总记录数，导致总是显示1页）
                pagination.value.total =  data.content.total;
            }else {
                message.error(data.message);
            }
        });
    };

    /** 表格点击页码时触发 */
    const handleTableChange = (pagination: any) => {
        //console输出对象，不能用“+”号连接，要用“,”号隔开，【点击第2页时输出效果】看看自带的分页参数都有啥： {current: 2, pageSize: 3, total: 5}
        console.log("看看自带的分页参数都有啥：" , pagination);
        // console.log("看看自带的分页参数都有啥：" + JSON.stringify(pagination));
        handleQuery({
            // 这里的pagination是一个普通的函数参数（可以随意命名，如pag），与onMounted中我们用的ref响应式变量不一样，所以不用x.value.y
            pageNum: pagination.current,
            pageSize: pagination.pageSize
        });
    };

    // ---- 表单 ----
    const categoryIds = ref(); // 数组，[100, 101]对应：前端开发 / Vue
    const ebook = ref();
    const modalVisible = ref(false);
    const modalLoading = ref(false);
    const handleModalOk = () => {
        modalLoading.value = true;
        ebook.value.category1Id = categoryIds.value[0] ;
        ebook.value.category2Id = categoryIds.value[1] ;
        axios.post("/ebook/save", ebook.value).then((response) => {
            modalLoading.value = false;
            const data = response.data ; // data = CommonResp
            // 后台返回成功后，关闭Modal弹框，并刷新列表
            if(data.success){
                modalVisible.value = false;
                // 重新刷新本页
                handleQuery({
                    // 这里的pagination用的是ref响应式变量
                    pageNum: pagination.value.current,
                    pageSize: pagination.value.pageSize
                });
            }else {
                message.error(data.message);
            }
        });
    }

    /** 编辑 */
    const handleEdit = (record: any) => {
        modalVisible.value = true ;
        // record是响应式变量ebooks中的记录，直接复制给ebook这个编辑框中的响应式变量，会导致编辑一半不保存直接关闭编辑框的情况下，ebooks中的记录也会跟着变化
        // ebook.value = record ;
        ebook.value = Tool.copy(record) ; // 用对象拷贝，解决响应式变量复制的影响
        categoryIds.value = [ebook.value.category1Id, ebook.value.category2Id] ;
        fileList.value = [] ; // 将页面显示的上传文件列表清空
    }

    /** 新增 */
    const handleAdd = () => {
        modalVisible.value = true ;
        ebook.value = {} ;
        categoryIds.value = [] ;
        fileList.value = [] ; // 将页面显示的上传文件列表清空
    }

    /** 删除 */
    const handleDelete = (id: number) => {
        axios.delete("/ebook/delete/" + id).then((response) => {
            const data = response.data ; // data = CommonResp
            // 删除成功后，刷新列表
            if(data.success){
                handleQuery({
                    pageNum: pagination.value.current,
                    pageSize: pagination.value.pageSize
                });
            }else {
                message.error(data.message);
            }
        });
    }

    const level1 = ref() ;
    let categorys: any ;
    /** 查询所有分类 */
    const handleQueryCategory = () => {
        loading.value = true;   // 加载中
        axios.get("/category/all").then((response) => {
            loading.value = false;  // 加载完成
            const data = response.data ;
            if(data.success){
                categorys = data.content ;
                console.log("原始数组:" , categorys) ;

                //level1.value = [] ; // 先初始化成一个空数组，再给它赋值树形结构
                level1.value = Tool.array2Tree(categorys, 0) ; // 父id为0，表示根节点，即一级分类
                console.log("树形结构:" , level1.value) ;

                // 加载完分类后，再加载电子书，否则如果分类树加载很慢，则电子书渲染会报错（列表中用到的getCategoryName方法需要上面的categorys）
                handleQuery({
                    pageNum: 1,
                    pageSize: pagination.value.pageSize
                }) ;
            }else {
                message.error(data.message);
            }
        });
    };

    // 根据分类Id，获取对应的分类名称
    const getCategoryName = (categoryId: any) => {
        let result = "" ;
        categorys.forEach((item: any) => {
            if(item.id === categoryId){
                // return item.name; // 注意，这里直接return不起作用
                result = item.name ;
            }
        }) ;
        return result ;
    };

    const fileList = ref() ; // 已经上传的文件列表
    // 上传中、完成、失败 都会调用这个函数
    const handleUploadChange = (info: any)=> {
        //console.log("handleUploadChange(), info参数", info) ;
        if(info.file.status === "uploading"){
            console.log("image uploading") ;
        }
        if(info.file.status === "done"){
            // 后台正常返回报文（返回码200），获取后端返回报文
            const response = info.file.response ;  // 注意跟axios请求返回的区别，这里不需要.data
            if(response.success){
                message.success("上传成功") ;
                ebook.value.cover = response.content ;  // 将后台返回的文件名，赋值给表单中的封面字段（用于后面提交表单使用）
            }else {
                message.error(response.message) ;
                fileList.value = [] ; // 将页面显示的上传文件列表清空
            }
        }else if(info.file.status === "error"){
            // 后台未正常返回报文（返回码非200）（可能登录校验拦截，也可能后台系统异常）
            if(info.file.error.status === 401){
                message.error("未登录或登录超时") ;
            }else{
                message.error("上传文件失败，请联系管理员") ;  // 该分支兜底，不会进入，因为后台有总体异常控制类，会对系统异常进行封装
            }
            fileList.value = [] ; // 将页面显示的上传文件列表清空
        }

    };

    onMounted(() => {
        handleQueryCategory() ;
    });

    return {
      param,
      ebooks,
      columns,
      pagination,
      loading,
      handleTableChange,
      handleQuery, // HTML中需要调用，所以要return出去
      getCategoryName,

      handleEdit,
      handleAdd,
      handleDelete,

      ebook,
      modalVisible,
      modalLoading,
      handleModalOk,
      categoryIds,
      level1,

      baseURL : process.env.VUE_APP_SERVER ,
      fileList,
      headers : {
        // 【遗留小问题，这里没用动态监听，在当前页面先登出再登录，其中的token不会动态刷新，还拿的旧token，后台登录校验会失败，需刷新当前页面】
        token: store.state.user.token  // 请求headers增加token（因为上传图片在后台进行了登录权限校验）
      },
      handleUploadChange
    };
  },

});
</script>

<style scoped>
    img {
        width: 50px;
        height: 50px;
    }
</style>